// Package wechat 实现微信支付 v3 客户端，包括请求签名、
// 响应验证和应用程序使用的高级 API 封装。
package wechat

import (
	"bytes"
	"context"
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"

	"github.com/we-pay-kit/we-pay-kit/internal/logger"
)

// Config 定义微信支付相关配置（用于请求签名、回包验签与重试策略）
type Config struct {
	AppID           string `json:"app_id"`
	MchID           string `json:"mch_id"`
	APIv3Key        string `json:"api_v3_key"`
	SerialNo        string `json:"serial_no"`
	NotifyURL       string `json:"notify_url"`
	PrivateKeyPEM   string `json:"mch_private_key"`
	RetryMax        int    `json:"retry_max"`
	RetryBaseMillis int    `json:"retry_base_ms"`
	RetryMaxMillis  int    `json:"retry_max_ms"`
}

// Client 微信支付客户端，封装HTTP调用、签名与验签逻辑
type Client struct {
	config  *Config
	client  *http.Client
	logger  *logger.Logger
	certMgr *CertificateManager
}

// NewClient 创建微信支付客户端
func NewClient(config *Config) *Client {
	l := logger.Get()
	client := &Client{
		config: config,
		client: &http.Client{
			Timeout: 30 * time.Second,
		},
		logger: l,
	}
	// Initialize certificate manager
	client.certMgr = NewCertificateManager(client)
	return client
}

// generateSignature 根据v3规范生成RSA-SHA256请求签名
func (c *Client) generateSignature(method, pathWithQuery, timestamp, nonceStr string, body []byte) (string, error) {
	// Build message: METHOD\npath?query\ntimestamp\nnonce\nbody\n
	msg := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n", strings.ToUpper(method), pathWithQuery, timestamp, nonceStr, string(body))

	// Parse private key
	block, _ := pem.Decode([]byte(c.config.PrivateKeyPEM))
	if block == nil {
		return "", fmt.Errorf("invalid merchant private key PEM")
	}
	pk, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		// try PKCS1
		if pkcs1, err2 := x509.ParsePKCS1PrivateKey(block.Bytes); err2 == nil {
			return signSHA256RSA(pkcs1, []byte(msg))
		}
		return "", fmt.Errorf("parse private key: %w", err)
	}
	rsaKey, ok := pk.(*rsa.PrivateKey)
	if !ok {
		return "", fmt.Errorf("private key is not RSA")
	}
	return signSHA256RSA(rsaKey, []byte(msg))
}

// signSHA256RSA 使用RSA-SHA256对数据进行签名并返回base64
func signSHA256RSA(priv *rsa.PrivateKey, data []byte) (string, error) {
	h := sha256.Sum256(data)
	sig, err := rsa.SignPKCS1v15(rand.Reader, priv, crypto.SHA256, h[:])
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(sig), nil
}

// doRequest 执行HTTP请求，自动签名、重试、读取与验签响应
func (c *Client) doRequest(method, url string, body interface{}) ([]byte, error) {
	var bodyBytes []byte
	var err error

	if body != nil {
		bodyBytes, err = json.Marshal(body)
		if err != nil {
			return nil, fmt.Errorf("failed to marshal request body: %w", err)
		}
	}

	req, err := http.NewRequest(method, url, bytes.NewReader(bodyBytes))
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	// 设置请求头
	timestamp := fmt.Sprintf("%d", time.Now().Unix())
	nonceBuf := make([]byte, 16)
	_, _ = rand.Read(nonceBuf)
	nonceStr := base64.StdEncoding.EncodeToString(nonceBuf)

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("User-Agent", "WePayKit/1.0")

	// 生成并设置签名
	pathWithQuery := req.URL.RequestURI()
	signature, err := c.generateSignature(method, pathWithQuery, timestamp, nonceStr, bodyBytes)
	if err != nil {
		return nil, err
	}
	authHeader := fmt.Sprintf(`WECHATPAY2-SHA256-RSA2048 mchid="%s",nonce_str="%s",timestamp="%s",serial_no="%s",signature="%s"`,
		c.config.MchID, nonceStr, timestamp, c.config.SerialNo, signature)
	req.Header.Set("Authorization", authHeader)

	// 发送请求（带指数退避重试）
	attempts := c.config.RetryMax
	if attempts <= 0 {
		attempts = 3
	}
	base := c.config.RetryBaseMillis
	if base <= 0 {
		base = 200
	}
	maxb := c.config.RetryMaxMillis
	if maxb <= 0 {
		maxb = 2000
	}
	var resp *http.Response
	for i := 0; i < attempts; i++ {
		resp, err = c.client.Do(req)
		if err == nil {
			break
		}
		sleep := time.Duration(base*(1<<i)) * time.Millisecond
		if int(sleep/time.Millisecond) > maxb {
			sleep = time.Duration(maxb) * time.Millisecond
		}
		time.Sleep(sleep)
	}
	if err != nil {
		return nil, fmt.Errorf("failed to send request: %w", err)
	}
	defer resp.Body.Close()

	// 读取响应
	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response body: %w", err)
	}

	// 检查响应状态
	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		return nil, fmt.Errorf("request failed with status %d: %s", resp.StatusCode, string(respBody))
	}
	// 验证回包签名
	if err := c.verifyResponse(resp, respBody); err != nil {
		return nil, fmt.Errorf("wechat response signature verify failed: %w", err)
	}

	return respBody, nil
}

// verifyResponse 验证微信v3回包签名，使用平台证书存储进行校验
func (c *Client) verifyResponse(resp *http.Response, body []byte) error {
	ts := resp.Header.Get("Wechatpay-Timestamp")
	nonce := resp.Header.Get("Wechatpay-Nonce")
	serial := resp.Header.Get("Wechatpay-Serial")
	sigB64 := resp.Header.Get("Wechatpay-Signature")
	if ts == "" || nonce == "" || serial == "" || sigB64 == "" {
		return fmt.Errorf("missing wechat response headers")
	}

	msg := fmt.Sprintf("%s\n%s\n%s\n", ts, nonce, string(body))
	sum := sha256.Sum256([]byte(msg))
	sig, err := base64.StdEncoding.DecodeString(sigB64)
	if err != nil {
		return fmt.Errorf("invalid signature base64: %w", err)
	}

	cert := GetPlatformCert(serial)
	if cert == nil {
		// Try to refresh certificates if not found
		c.logger.Info(fmt.Sprintf("Certificate not found for serial %s, attempting refresh", serial))
		if c.certMgr != nil {
			if err := c.certMgr.TriggerRefreshIfNeeded(serial); err != nil {
				c.logger.Error(fmt.Sprintf("Certificate refresh failed: %v", err))
			}
			// Try again after refresh
			cert = GetPlatformCert(serial)
		}
		if cert == nil {
			return fmt.Errorf("platform certificate not found for serial %s", serial)
		}
	}

	pub, ok := cert.PublicKey.(*rsa.PublicKey)
	if !ok {
		return fmt.Errorf("platform public key is not RSA")
	}
	if err := rsa.VerifyPKCS1v15(pub, crypto.SHA256, sum[:], sig); err != nil {
		return fmt.Errorf("signature verification failed: %w", err)
	}
	return nil
}

// StartCertificateManager starts automatic certificate management
func (c *Client) StartCertificateManager(ctx context.Context) {
	if c.certMgr != nil {
		go c.certMgr.StartAutoRefresh(ctx)
		c.logger.Info("Certificate manager started")
	}
}

// GetCertificateManager returns the certificate manager
func (c *Client) GetCertificateManager() *CertificateManager {
	return c.certMgr
}

// Close 关闭客户端
func (c *Client) Close() {
	// 清理资源
	c.logger.Info("WeChat pay client closed")
}
